package wcg;

import ikelib.ChannelData;
import ikelib.MyDirectory;
import ikelib.SecondBlock;
import ikelib.unsigned_byte;
import ikelib.wintime;
import ikelib.wintools;

import java.awt.Cursor;
import java.awt.Point;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTargetDropEvent;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PrintWriter;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TimeZone;
import java.util.TreeMap;
import java.util.TreeSet;

import javax.swing.JOptionPane;
import javax.swing.ProgressMonitor;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

import GraphicView.GraphicView;




public class winDataBase {
	private TreeMap<Integer,ChannelDataCollection> Data = null;
	private TreeMap<Integer,ChannelOutlineDataCollection> Data1 = null;
	private TreeMap<Integer,ChannelOutlineDataCollection> Data60 = null;
	private ArrayList<Integer> Channel = null;
	private TreeMap<Integer,Chproperty> ChannelProp = null;
	private winDataBaseChange Listener = null;
	public  Object Lock = new Object();
	public  GraphicView DataSetter = null;
	public wintime LatestTime = new wintime();
	public wintime OldestTime = new wintime();
	private String WriteOutFolder = new String("");
	public wintime LatestSwapTime = new wintime();
	public TreeSet<winFileDataBase> FileDataBase = new TreeSet<winFileDataBase>();

	public long MemoryRemain = 0; 
	public long MemoryMax = 0;
	public long MemoryCheckTime = 0;
	
	private wintime DispStartTime = new wintime();
	private long    DispSpan = 0;
	private wintime DispEndTime = new wintime();
	public wintime DispStartTimeSec = new wintime();
	public long    DispSpanSec = 0;
	
	private ProgressDialog fileReadingProgressDialog = null;
//	private abortDialogThread  AbThread = null;
	//private boolean Executing = false;
	
	//-----------------------------------------------
	//--
	//--
	public winDataBase(){
		Data = new TreeMap<Integer,ChannelDataCollection>();
		Data1 = new TreeMap<Integer,ChannelOutlineDataCollection>();
		Data60 = new TreeMap<Integer,ChannelOutlineDataCollection>();
		Channel = new ArrayList<Integer>();
		ChannelProp = new TreeMap<Integer,Chproperty>();
		fileReadingProgressDialog = new ProgressDialog(null);
		
		memoryManagement(1);
	}
	public wintime getDispStartTime(){
		return DispStartTime;
	}
	/**
	 * chを指定してDispStartTimeからはじめて最初に見つかった時刻を返す。
	 * @param ch
	 * @return
	 */
	public wintime getDispStartTime(int ch){

		ChannelDataCollection dcol = getChannelDataCollection(ch);
		if(dcol==null) return null;	// そのＣＨはない
		wintime st = new wintime(getDispStartTime());
		wintime et = new wintime(getDispEndTime());
		while(true){
			ChannelData cd = dcol.getChannelData(st);
			if(cd != null) return st;
			if(st.getValueS()>=et.getValueS()) break;
			st.inc_sec();
		}
		return null;
	}
	public wintime getDispEndTime(){
		return DispEndTime;
	}
	public void showWinFileDataBase(){
		Iterator<winFileDataBase> ite = FileDataBase.iterator();
		while(ite.hasNext()){
			winFileDataBase db = ite.next();
			String onmem;
			if(db.isOnMemory()) onmem = "on ";
			else				onmem = "off";
			System.out.println(String.format("%s [ %s ] %s - %s", db.getFileName(),onmem,db.getOldest().fmt(),db.getLatest().fmt()));
		}
	}
	// 表示したい範囲をセット（表示領域以外はパージの対象になる）
	public void setDispRange(wintime start,long span,boolean reload){
		DispStartTimeSec = new wintime(start);
		DispSpanSec      = span;
		wintime st  = new wintime(start.year(),start.month(),start.day(),start.hour(),start.min(),0,0); // 分単位に丸める
		//DispStartTime = new wintime(start.year(),start.month(),start.day(),start.hour(),start.min(),0,0); // 分単位に丸める
		long spn = (span+59)/60;
		if(st.compare(DispStartTime)==0 && spn==DispSpan) return;
		DispStartTime = st;
		DispSpan = spn;
		DispEndTime = new wintime(DispStartTime);
		DispEndTime.inc_sec((int)span+60);
		if(reload){
			reloadData(DispStartTime,DispEndTime);
		}
	}
	// --------------------------------------------
	// 
	/**
	 * メモリの残容量をしらべて、少なければデータを古いものから削除する。
	 * @param minimunKByte
	 * @return
	 */
	private boolean memoryManagement(int minimunKByte){
		
		//if(minimunKByte<100000) return true;
		Date dt = new Date();
		long time = dt.getTime();

		long maxmem = Runtime.getRuntime().maxMemory() / 1024; // 最大メモリ
		long totalmem = Runtime.getRuntime().totalMemory() / 1024; // 現在の使用メモリ
		long freemem = Runtime.getRuntime().freeMemory() / 1024; // 現在の空きメモリ
		long remain = maxmem - (totalmem - freemem);	// 最大メモリまで拡張した時の空きメモリ
		
		fileReadingProgressDialog.getProgressBarMemory().setValue((int)(100*freemem/maxmem));

		long delmem=0L;
		//System.out.println(String.format("Memory manage Free=%dKB,Remain=%dKB",(int)freemem,(int)remain));
		MemoryRemain = remain;
		MemoryMax = maxmem;
		if(minimunKByte < (int)(maxmem/5)){
			minimunKByte  = (int)(maxmem/5);
		}
		int reqmemory = minimunKByte;	// 要求されたメモリ KB
		int delmemory = reqmemory;		// 削除しようとするメモリ KB
		//System.out.println(String.format("Memory manage:Req=%dKB,Del=%dKB",(int)reqmemory,(int)delmemory));
		if(remain <= reqmemory){
			//System.out.println(String.format("Memory manage Request=%,dKB Free=%,dKB,",(int)minimunKByte,(int)freemem));
			MemoryCheckTime=time;
			//System.out.println(String.format("remain %d = max %d - total %d + free %d",remain,maxmem,totalmem,freemem));
			while(delmem < delmemory*1024){
				int del = deleteData(60*60); // 引数の秒分　データをパージ 　パージしたバイト数を返す
				if(del==0) break;
				delmem += del;
			}
			System.gc(); // ガベージコレクション
			//System.out.println(String.format("Gabage collection Free=%,dKB Delete=%,dKB", Runtime.getRuntime().freeMemory() / 1024,delmem/1024));
		}
		freemem = Runtime.getRuntime().freeMemory() / 1024;
		remain = maxmem - (totalmem - freemem);	
		if(remain < minimunKByte){
			String mes = String.format("Error: Too less memory.\n Please assign more memory.\n (Request=%,d KB  Free=%,d KB )",minimunKByte,remain);
			JOptionPane.showConfirmDialog(null, mes, "Error",
				JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE);
			return false;
		}

		return true;
	}
	//-----------------------------------------------
	// 指定秒数のデータを削除する
	//--
	private int deleteData(int delsec){
		// すべてのｃｈから
		//System.out.println("delete-----------------");
		int deletedByte=0;
		// 表示している時間を求めて
		long oldDisp = DispStartTime.getValueS();
		long newDisp = oldDisp ;
		long oldData = OldestTime.getValueS();
		long newData = LatestTime.getValueS();
		// どちら側からデータを削除するか決める
		boolean fromold=true;	// 最古から削除
		if(oldDisp<=oldData && newData <= newDisp) return 0; // 削除出来ない
		if(oldDisp<=oldData && newDisp <= newData){
			fromold=false; // 最新から削除
		}
		else if(oldData<=oldDisp && newDisp <= newData){ // 表示エリアがデータエリアの内部の場合
			if(oldDisp-oldData < newData-newDisp){ // 表示エリアからより離れているほうを削除の対象にする
				fromold=false;		
			}
			else fromold = true;
		}
		// データをpurgeする
		deletedByte = purgeData(delsec,fromold);
		//System.out.println(String.format("purge %,dKB",deletedByte*4/1024));
		return deletedByte*4;
	}
	// ---------------------------------------------------------
	// 指定の時刻を含むファイルを再ロードする
	private void reloadData(wintime nt,wintime nt2){
		wintime newtime = new wintime(nt.year(),nt.month(),nt.day(),nt.hour(),nt.min(),nt.sec(),0); 
		wintime newtime2 = new wintime(nt2.year(),nt2.month(),nt2.day(),nt2.hour(),nt2.min(),nt2.sec(),0); 
		//System.out.println(String.format("Re-load time %s",newtime.fmt()));
		HashSet<String> loadfiles = new HashSet<String>();
		Iterator<winFileDataBase> ite2  = FileDataBase.iterator();
		while(ite2.hasNext()){
			winFileDataBase db = ite2.next();
			if(db.isContainTimeSpan(newtime,newtime2)){
				if(!db.isOnMemory()){
					String filename = db.getFileName();
					loadfiles.add(filename);
				}
			}
		}
		Iterator<String> ite = loadfiles.iterator();
		winDataBaseChange lsn = Listener;
		GraphicView gv = DataSetter;
		Listener = null;
		DataSetter = null;
		while(ite.hasNext()){
			String file = ite.next();
			//System.out.println(String.format("Re-load %s",file));
			addChDataTh(file);
		}
		Listener = lsn;
		DataSetter = gv;
	}
	// ---------------------------------------------------------
	// OldestTime またはLatestTimeから sec　秒分のデータを削除し、
	// FileDataBaseにonMemory=falseとしてマークする
	private int purgeData(int sec,boolean fromold){
		int deletedByte=0;
		//System.out.println(String.format("purgeData %d s,old=", sec)+fromold);
		// ch毎のループ
		Iterator<Integer> ite = iteratorChannel();
		while(ite.hasNext()){
			int ch =ite.next();
			ChannelDataCollection dcol = getChannelDataCollection(ch);
		    TreeMap<Integer,ChannelData> cdlist = dcol.getChannelData();
			if(!cdlist.isEmpty()){
				wintime tm;
				wintime tm1st=null;
				wintime tmend = null;
				if(fromold) {
					tm = new wintime(OldestTime);
				}
				else{
					tm = new wintime(LatestTime);
				}
				int state = 0;
				for(int i=0;i<sec;){
					int tmint = tm.getValueS();
					switch(state){
					case 0:
						if(cdlist.containsKey(tmint)){
							ChannelData cd = cdlist.get(tmint);
//							if(cd.getData()!=null){
//								deletedByte += cd.getData().length;
//								cd.removeData();
//							}
							deletedByte += cd.getData().length;
							cdlist.remove(tmint);
							//System.out.println("remove data"+tm.fmt());
							tm1st = new wintime(tm);
							state = 1;
							i++;
						}
						break;
					case 1:
						i++;
						if(cdlist.containsKey(tmint)){
							ChannelData cd = cdlist.get(tmint);
//							if(cd.getData()!=null){
//								deletedByte += cd.getData().length;
//								cd.removeData();
//							}
							deletedByte += cd.getData().length;
							cdlist.remove(tmint);
							//System.out.println("remove data"+tm.fmt());
							tmend = new wintime(tm);
						}
						else {
							state=2;
						}
						break;
					default:
						i++;
					}
					if(fromold)	tm.inc_sec();
					else		tm.dec_sec();
				}
				//System.out.println("remove data"+tm1st.fmt()+" - "+tmend.fmt());
				if(tm1st!=null){
					Iterator<winFileDataBase> ite2  = FileDataBase.iterator();
					while(ite2.hasNext()){
						winFileDataBase db = ite2.next();
						if(db.isOnMemory()){
							if(db.isContainCh(ch)){
								if(db.isContainTimeSpan(tm1st,tmend)){
									db.setOnMemory(false);
									//System.out.println("Swap Out"+db.getFileName()+"-"+tm1st.fmt()+"-"+tmend.fmt());
								}
							}
						}
					}
				}
			}
		}
		return  deletedByte;
	}
	//----------------------------------------------------------------------
	// 再読み込みに備えてwinファイルを記憶するためのクラス
	class winFileDataBase implements Comparable{
		private int fileType; // 0:A0 5:A5
		public void setFileType(int ft){
			fileType = ft;
		}
		public int getFileType(){
			return fileType;
		}
		private boolean OnMemory ;
		private String Filename;
		private wintime Oldest = null;
		
		public wintime getOldest() {
			return Oldest;
		}

		public wintime getLatest() {
			return Latest;
		}

		private int OldestSec;
		private wintime Latest = null;
		private int LatestSec;
		private TreeSet<Integer> ChannelList = new TreeSet<Integer>();
		winFileDataBase(){
		}
		
		public void set(wintime ot,wintime nt){
			Oldest = new wintime(ot);
			OldestSec = Oldest.getValueS();
			Latest = new wintime(nt);
			LatestSec = Latest.getValueS();
		}
		public void set(String fname,boolean onmem){
			OnMemory = onmem;
			Filename = new String(fname);
		}
		public void setOnMemory(boolean onmem){
			OnMemory = onmem;
		}
		public boolean isOnMemory(){
			return OnMemory;
		}
		public void addch(int ch){
			ChannelList.add(ch);
		}
		public boolean isContainTime(wintime tm){
			int time = tm.getValueS();
			if(OldestSec<=time && time<=LatestSec){
				return true;
			}
			return false;
		}
		public boolean isContainTimeSpan(wintime tm,wintime tm2){
			int time = tm.getValueS();
			int time2 = tm2.getValueS();
			if(time > time2){
				int tmtmp = time;
				time = time2;
				time2 = tmtmp;
			}
			if(time2 < OldestSec || LatestSec < time){
				return false;
			}
			return true;
		}
		public boolean isContainCh(int ch){
			return ChannelList.contains(ch);
		}
		public String getFileName(){
			return Filename;
		}
		@Override
		public int compareTo(Object arg0) {
			winFileDataBase e = (winFileDataBase)arg0;
			int ret = Filename.compareToIgnoreCase(e.getFileName());
			if(ret!=0){
				return ret;
			}
			ret = fileType - e.getFileType();
			if(ret!=0){
				return ret;
			}
			if(OnMemory != e.isOnMemory()){
				if(OnMemory) return 1;
				else return -1;
			}
			ret = OldestSec - e.getOldest().getValueS();
			if(ret!=0){
				return ret;
			}
			ret = LatestSec - e.getLatest().getValueS();
			if(ret!=0){
				return ret;
			}
			
			return 0;
		}
	}
	//------------------------------------------------------
	// ファイル出力フォルダ
	public void setWriteOutFolder(String dir){
		synchronized(Lock){
			WriteOutFolder = dir;
		}
		if(Listener!=null) Listener.OnChangeDataBase(winDataBaseChange.EVENT_SAVE_FOLDER_CHANGE,0,null);
	}
	public String getWriteOutFolder(){
		return WriteOutFolder;
	}
	//------------------------------------------------------
	// チャネル属性定義
	class Chproperty {
		public int FullScale;// full scale int値
	}
	public int getFullScale(int ch){
		Chproperty p = ChannelProp.get(ch);
		if(p==null) return 	8388607;
		return p.FullScale;
	}
	public void setFullScale(int ch,int fullscale){
		Chproperty p = ChannelProp.get(ch);
		if(p==null){
			p = new Chproperty();
		}
		p.FullScale = fullscale;
		ChannelProp.put(ch,p);
	}
	
//	public TreeMap<Integer,ChannelDataCollection> getData(){
//		return Data;
//	}
//	public TreeSet<Integer> getChannel(){
//		return Channel;
//	}
	//
	// channel の順番を変更する
	//
	public synchronized void setChannelSequence(ArrayList<Integer> list){
		ArrayList<Integer> nlist=new ArrayList<Integer>();
		Iterator<Integer> ite = list.iterator();
		while(ite.hasNext()){
			Integer fch = ite.next();
			if(Channel.contains(fch)){
				nlist.add(fch);
				Channel.remove((Object)fch);
			}
		}
		Iterator<Integer> ite2 = Channel.iterator();
		while(ite2.hasNext()){
			Integer ch = ite2.next();
			nlist.add(ch);
		}
		Channel = nlist;
	}
	public boolean isEmptyChannel(){
		return Channel.isEmpty();
	}
	public int firstChannel(){
		return Channel.get(0);
		//return Channel.first();
	}
	public int sizeChannel(){
		return Channel.size();
	}
	public void clearChannel(){
		synchronized(Lock){
			Channel.clear();
		}
		if(Listener!=null) Listener.OnChangeDataBase(winDataBaseChange.EVENT_CHANNEL_CLEAR,0,null);
	}
	public void addChannel(int ch){
		boolean flag = false;
		synchronized(Lock){
			if(!Channel.contains(ch)){
				if(Channel.add(ch)){
					flag = true;
				}
			}
		}
		if( flag ){
			if(Listener!=null) Listener.OnChangeDataBase(winDataBaseChange.EVENT_CHANNEL_ADD,ch,null);
		}
	}
	
	private int getChannelIndex(int ch){
		Iterator<Integer> ite = Channel.iterator();
		int idx = 0;
		while(ite.hasNext()){
			int c = ite.next();
			if(c==ch) return idx;
			idx++;
		}
		return -1;
	}
	public void removeChannel(int ch){
		synchronized(Lock){
			int idx = getChannelIndex(ch);
			Channel.remove(idx);
		}
		if(Listener!=null) Listener.OnChangeDataBase(winDataBaseChange.EVENT_CHANNEL_REMOVE,ch,null);
	}
	/**
	 * chsorのｃｈ削除しchdesの位置に挿入する。
	 * @param chsor
	 * @param chdes
	 */
	public void moveChannel(int chsor,int chdes){
		synchronized(Lock){
			int idxsor = getChannelIndex(chsor);
			int idxdes = getChannelIndex(chdes);
			Channel.remove(idxsor);
			Channel.add(idxdes,chsor);
		}
		if(Listener!=null) Listener.OnChangeDataBase(winDataBaseChange.EVENT_CHANNEL_ORDER_CHANGE,0,null);
	}
	public Iterator<Integer> iteratorChannel(){
		return Channel.iterator();
	}
	
	private void clearChannelData(int ch){
		removeChannel(ch);
		removeData(ch);
		if(Listener!=null) Listener.OnChangeDataBase(winDataBaseChange.EVENT_CHANNEL_REMOVE,ch,null);
	}
	public void clearData(){
		Channel.clear();
		Data.clear();
		Data1.clear();
		Data60.clear();
		if(Listener!=null) Listener.OnChangeDataBase(winDataBaseChange.EVENT_DATA_CLEAR,0,null);
		LatestTime = new wintime();
		OldestTime = new wintime();
		FileDataBase.clear();
	}
	public void clearData60(){
		Data60.clear();
		if(Listener!=null) Listener.OnChangeDataBase(winDataBaseChange.EVENT_DATA_CLEAR,0,null);
	}
	public void clearData1(){
		Data1.clear();
		if(Listener!=null) Listener.OnChangeDataBase(winDataBaseChange.EVENT_DATA_CLEAR,0,null);
	}
	public void putData(int ch,ChannelDataCollection dcol){
		Data.put(ch, dcol);
		if(Listener!=null) Listener.OnChangeDataBase(winDataBaseChange.EVENT_DATA_PUT,ch,dcol);
	}
	public void putData1(int ch,ChannelOutlineDataCollection dcol){
		Data1.put(ch, dcol);
		//if(Listener!=null) Listener.OnChangeDataBase(winDataBaseChange.EVENT_DATA_PUT,ch,dcol);
	}
	public void putData60(int ch,ChannelOutlineDataCollection dcol){
		Data60.put(ch, dcol);
		//if(Listener!=null) Listener.OnChangeDataBase(winDataBaseChange.EVENT_DATA_PUT,ch,dcol);
	}
	public void removeData(int ch){
		Data.remove(ch);
		if(Listener!=null) Listener.OnChangeDataBase(winDataBaseChange.EVENT_DATA_REMOVE,ch,null);
	}
	public void removeData1(int ch){
		Data1.remove(ch);
		if(Listener!=null) Listener.OnChangeDataBase(winDataBaseChange.EVENT_DATA_REMOVE,ch,null);
	}
	public void removeData60(int ch){
		Data60.remove(ch);
		if(Listener!=null) Listener.OnChangeDataBase(winDataBaseChange.EVENT_DATA_REMOVE,ch,null);
	}
	/**
	 * DataChannelCollectionを返す
	 * @param ch　int Channel ID
	 * @return DataCollenction 
	 */
	public ChannelDataCollection getChannelDataCollection(int ch){
		return Data.get(ch);
	}
	public ChannelOutlineDataCollection getChannelDataCollection60(int ch){
		return Data60.get(ch);
	}
	public ChannelOutlineDataCollection getChannelDataCollection1(int ch){
		return Data1.get(ch);
	}
	public void addChangeListener(winDataBaseChange p){
		Listener = p;
	}
	//--------------------------------------------------------------------------------
	private static int findFiles(String fname,TreeSet<String> ts){
		int count = 0;
		File f = new File(fname);
		if(f.isFile()){
			ts.add(f.getAbsolutePath());
			count++;
			//System.out.println("Dropped file:"+f.getAbsolutePath());
		}
		else if(f.isDirectory()){
			String[] ret = f.list(new FilenameFilter(){
				public boolean accept(File dir, String name){
					return true;
				}
			});
			for(int j=0;j<ret.length;j++){
				findFiles(fname + new String(f.separator) + ret[j],ts);
			}
		}
		return count;
	}
	//--------------------------------------------------------------------------------
	// フォルダを含むファイルを個別のファイル名に変換する
	public static TreeSet<String> Dir2Files(String[] dir){
		TreeSet<String> ts=null;
		ts = new TreeSet<String>();
		for(int i=0;i<dir.length;i++){
			findFiles(dir[i],ts);
		}
		return ts;
	}
	//--------------------------------------------------------------------------------
	// フォルダを含むファイルを個別のファイル名に変換する
	public static TreeSet<String> Dir2Files(File[] file){
		String[] dir = new String[file.length];
		for(int i=0;i<file.length;i++){
			dir[i] = file[i].getPath();
		}
		return Dir2Files(dir);	
	}
	
	//--------------------------------------------------------------------------------
	// ファイルドロップされたときファイル名に分解する
	public   TreeSet<String> Dir2Files(DropTargetDropEvent d) {
		Transferable t = d.getTransferable();
		TreeSet<String> ts=null;
		if(t.isDataFlavorSupported(DataFlavor.javaFileListFlavor)){
			// ACTION_REFERENCE
			d.acceptDrop(DnDConstants.ACTION_REFERENCE);
          	ts = new TreeSet<String>();
			try{
				List<File> list=null;
				boolean bLinux=false;
				if(MyDirectory.getOsName() == MyDirectory.OSName.LINUX) bLinux=true;
				if(bLinux){
					DataFlavor nixFileDataFlavor = new DataFlavor("text/uri-list;class=java.lang.String");
					String data = (String)t.getTransferData(nixFileDataFlavor);
					for(StringTokenizer st = new StringTokenizer(data, "\r\n"); st.hasMoreTokens();){
					    String token = st.nextToken().trim();
					    if(token.startsWith("#") || token.isEmpty()){
					         continue;
					    }
				        File file = new File(new URI(token));
				        findFiles(file.getAbsolutePath(),ts);
					}					
				}
				else{
					list = (List<File>)t.getTransferData(DataFlavor.javaFileListFlavor);
					for(int i=0;i<list.size();i++){
						findFiles(list.get(i).getAbsolutePath(),ts);
					}
				}
				d.dropComplete(true);
			}catch(Exception e){
				throw new RuntimeException(e);
			}

 		}
		if(ts!=null){
			addHistory(ts); // ドロップしたファイルはRecent Filesに加える。
		}
		return ts;
	}
	/// 過去に開いたファイルを管理するために
	private ArrayList<TreeSet<String>> OpenHistory = new ArrayList<TreeSet<String>>();
	/**
	 * ヒストリに追加
	 * @param ts
	 */
	public void addHistory(TreeSet<String> ts){
		OpenHistory.add(ts);
	}
	/**
	 * ヒストリを表す文字列を返す。
	 * @param index
	 * @return
	 */
	public String getHistoryName(int index){
		if(index>=OpenHistory.size()) return null;
		TreeSet<String> ts = OpenHistory.get(index);
		String name = ts.first();
		if(name.length()>83){
			name = name.substring(0,40) + "..." + name.substring(name.length()-40,name.length());
		}
		if(ts.size()>1){
			String lst = ts.last();
			if(lst.length()>33){
				lst = "..." + lst.substring(lst.length()-30,lst.length());
			}
			name = "[" + ts.size() + "files]" + name + " +...+" + lst;
		}
		return name;
	}
	public int getHistoryNumber(){
		return OpenHistory.size();
	}
	public TreeSet<String> getHistory(int index){
		if(index>=OpenHistory.size()) return null;
		return OpenHistory.get(index);
	}
	
	//--------------------------------------------------------------------------------
//	public void addChData(TreeSet<String> ts){
//		if(DataSetter!=null)  DataSetter.addChData(ts);
//	}	

	//--------------------------------------------------------------------------------
	//--------------------------------------------------------------------------------
	private void buildChData60(int ch,wintime tm){
		ChannelDataCollection dcol = getChannelDataCollection(ch);
		if(dcol==null) return;
		wintime wtstart = new wintime(tm.year(),tm.month(),tm.day(),tm.hour(),tm.min(),0,0);
		wintime wt = new wintime(wtstart);
		int max=Integer.MIN_VALUE;
		int min=Integer.MAX_VALUE;
		ChannelData cd;
		double mean=0.0;
		for(int i=0;i<60;i++){
			cd = dcol.getChannelData(wt);
			if(cd == null) return;
			if(max<cd.getMax()) max = cd.getmax();
			if(min>cd.getMin()) min = cd.getmin();
			mean += cd.getMean();
			wt.inc_sec();
		}
		mean /= 60.0;
		wt = new wintime(wtstart);
		double val = 0.0;
		int smplnum = 0;
		for(int i=0;i<60;i++){
			cd = dcol.getChannelData(wt);
			int[] dat = cd.getData();
			for(int j=0;j<dat.length;j++){
				val += ((double)dat[j] - mean)*((double)dat[j] - mean);
				smplnum++;
			}
			wt.inc_sec();
		}
		val /=  (double)(smplnum-1);
	
		ChannelOutlineDataCollection dcol60 = Data60.get(ch);
		if(dcol60 == null){
			dcol60 = new ChannelOutlineDataCollection();
			//System.out.println("Create Datacollection60");
		}
		wt = new wintime(wtstart);
		ChannelOutlineData cd60 = dcol60.getChannelData(wt);
//		if(cd60==null){
			cd60 = new ChannelOutlineData(mean,min,max,val,wt,smplnum);
			dcol60.addChannelData(cd60);
			//System.out.println(String.format("Create ChannelData60 %d %f %d %d %f,%s",ch,mean,min,max,val,wt.fmt()));
//		}
//		else{
//			if(mean != cd60.getMean() || min != cd60.getMin() || max != cd60.getMax() || val != cd60.getValiance()||smplnum != cd60.getSamplerate()){
//				System.out.println("Another value channel data60  already exist");
//			}
//		}
		Data60.put(ch, dcol60);
		
	}
	private void buildChData1(int ch,ChannelData cd){
		ChannelOutlineDataCollection dcol1 = Data1.get(ch);
		if(dcol1 == null){
			dcol1 = new ChannelOutlineDataCollection();
			//System.out.println("Create Datacollection60");
		}
		wintime wt = new wintime(cd.getTime());
		ChannelOutlineData cd1 = dcol1.getChannelData(wt);
		double mean = cd.getMean();
		int min = cd.getmin();
		int max = cd.getmax();
		double val = cd.getVariance();
		int smplnum = cd.getSamplerate();
//		if(cd1==null){
			cd1 = new ChannelOutlineData(mean,min,max,val,wt,smplnum);
			dcol1.addChannelData(cd1);
			//System.out.println(String.format("Create ChannelData60 %d %f %d %d %f,%s",ch,mean,min,max,val,wt.fmt()));
//		}
//		else{
//			System.out.println("channel data1  already exist!");
//		}
		Data1.put(ch, dcol1);
		
	}
	//------------------------------------------------
	// ＣＨデータを追加
	public synchronized void addChData(byte[] chdata){
		addChData(chdata,null);
	}
	
	/**
	 * A0タイプのデータを追加
	 * @param chdata
	 * @param db
	 * @return
	 */
	private boolean addChDataA0(byte[] chdata,winFileDataBase db) {
		int max = chdata.length;
		wintime sm=new wintime();
		wintime tm=new wintime();
		boolean first=true;
		for(int offset=0;offset<max;){
			fileReadingProgressDialog.setText2(String.format("%,d Byte %3.1f%% (A0)",offset,100f*(float)offset/max));
			fileReadingProgressDialog.getProgressBar2().setValue(offset);
			
			SecondBlock blk = new SecondBlock(chdata,offset);
			offset += blk.length();
			tm = blk.getWinTime();
			if(first){
				sm = new wintime(tm);
				first=false;
			}
			//	if(tm.getValueS()>LatestTime.getValueS()){
			if(LatestTime.year()==0 || tm.getValueS()>LatestTime.getValueS()){
				LatestTime = new wintime(tm);
				//LatestTime.inc_sec();
			}
			if(OldestTime.year()==0 || tm.getValueS()<OldestTime.getValueS()){
				OldestTime = new wintime(tm);
			}
			Iterator<ChannelData> ite = blk.getChannelDataList().iterator();
			while(ite.hasNext()){
				ChannelData d = ite.next();
				int ch = d.getChannelName();
				//System.out.println("CH=" + ch );
				addChannel(ch);
				if(db != null) {
					db.addch(ch);
				}
				ChannelDataCollection dcol = getChannelDataCollection(ch);
				if(dcol == null){
					dcol = new ChannelDataCollection();
				}
				dcol.addChannelData(d);
				putData(ch,dcol);
				
				buildChData1(ch,d);
				
				// ６０秒分がそろった可能性がある
				if(tm.sec()==59){
					buildChData60(ch,tm);
				}
				if(fileReadingProgressDialog.isCanceled()){
					if(DataSetter!=null) DataSetter.afterAddBlockData(chdata);
					return false;
				}
//				if(AbThread.EndFlag) break;

			}
		}
		if(db != null)  db.set(sm, tm);
		return true;
	}
	/**
	 * A5タイプのデータを追加
	 * @param chdata
	 * @param db
	 * @return
	 */
	private boolean addChDataA5(byte[] chdata,winFileDataBase db) {
		
		ArrayList<SecondBlock> blklist = new ArrayList<SecondBlock>();
		
		int max = chdata.length;
		wintime sm=new wintime();
		wintime tm=new wintime();
		boolean first=true;
		for(int offset=0;offset<max;){
			if(fileReadingProgressDialog.isCanceled()){
				if(DataSetter!=null) DataSetter.afterAddBlockData(chdata);
				return false;
			}
			fileReadingProgressDialog.setText2(String.format("%,d Byte %3.1f%% (A5)",offset,100f*(float)offset/max));
			fileReadingProgressDialog.getProgressBar2().setValue(offset);
			SecondBlock blk = new SecondBlock(chdata,offset,5);
			offset += blk.length();
			tm = blk.getWinTime();
			blklist.add(blk);
			if(first){
				sm = new wintime(tm);
				first=false;
			}
			if(tm.msec() == 0){
				if(LatestTime.year()==0 || tm.getValueS()>LatestTime.getValueS()){
					LatestTime = new wintime(tm);
				}
				if(OldestTime.year()==0 || tm.getValueS()<OldestTime.getValueS()){
					OldestTime = new wintime(tm);
				}
			}
			else if(tm.msec() == 900){
				// ｃｈの数を求める
				TreeSet<Integer> chtree = new TreeSet<Integer>();
				for(int i=0;i<blklist.size();i++){
					blk = blklist.get(i);
					Iterator<ChannelData> ite = blk.getChannelDataList().iterator();
					while(ite.hasNext()){
						ChannelData d = ite.next();
						int ch = d.getChannelName();
						chtree.add(ch);
					}
				}
				// データをｃｈ、時間ばらばらにする
				ChannelData[][] cd = new ChannelData[chtree.size()][10];
				for(int i=0;i<blklist.size();i++){
					blk = blklist.get(i);
					Iterator<ChannelData> ite = blk.getChannelDataList().iterator();
					while(ite.hasNext()){
						ChannelData d = ite.next();
						int ch = d.getChannelName();
						int index=0;
						Iterator<Integer> ite2  = chtree.iterator();
						while(ite2.hasNext()){
							int ch2 = ite2.next();
							if(ch2==ch) break;
							index++;
						}
						cd[index][i]=d;
					}
				}
				// channel毎にループ
				for(int ch=0;ch<chtree.size();ch++){
					ArrayList<ChannelData> ad = new ArrayList<ChannelData>();
					for(int j=0;j<10;j++){
						ad.add(cd[ch][j]);
					}
					// １秒分まとめる
					ChannelData d = ChannelData.assembleChannelData(ad);
					// 同じ処理
					addChannel(ch);
					if(db != null) {
						db.addch(ch);
					}
					ChannelDataCollection dcol = getChannelDataCollection(ch);
					if(dcol == null){
						dcol = new ChannelDataCollection();
					}
					dcol.addChannelData(d);
					putData(ch,dcol);
					
					buildChData1(ch,d);
					
					// ６０秒分がそろった可能性がある
					if(d.getTime().sec()==59){
						buildChData60(ch,tm);
					}
					
				}
				blklist.clear();
			}
		}
		if(db != null)  db.set(sm, tm);
		return true;
	}
	
	
	/**
	 * K-NET TEXT形式からaddする(1秒刻みを仮定している）
	 * @param chdata
	 * @param db
	 * @return
	 */
	private boolean addChDataKNETascii(byte[] chdata,winFileDataBase db) {
		
		KNET_ascii knet = new KNET_ascii(chdata);
		if(knet.Data==null) return false;
		
		wintime sm = new wintime(knet.StartTime);
		wintime tm = new wintime(sm);
		tm.inc_sec(knet.Data.length/knet.SampleRate-1);

		if(LatestTime.year()==0 || tm.getValueS()>LatestTime.getValueS()){
			LatestTime = new wintime(tm);
		}
		if(OldestTime.year()==0 || sm.getValueS()<OldestTime.getValueS()){
			OldestTime = new wintime(sm);
		}
		
		int ch = knet.Channel;
	
		ChannelData[] cd = ChannelData.makeDataGeneric(ch, knet.Data, sm, knet.SampleRate);
		ChannelDataCollection dcol = getChannelDataCollection(ch);
	
		if(dcol == null){
			dcol = new ChannelDataCollection();
		}
		
		addChannel(ch);
		if(db != null) {
			db.addch(ch);
		}

		for(int i=0;i<cd.length;i++){
			dcol.addChannelData(cd[i]);
			putData(ch,dcol);
			
			buildChData1(ch,cd[i]);
			
			if(cd[i].getTime().sec()==59){
				buildChData60(ch,cd[i].getTime());
			}
		}
		
		if(db != null)  db.set(sm, tm);

		if(Listener!=null) Listener.OnAddKNETChannelInfo(winDataBaseChange.EVENT_KNET_INFO_ADD,ch,knet);

		return true;
	}
	/**
	 * K-NET CNT形式からaddする(1秒刻みを仮定している）
	 * @param chdata
	 * @param db
	 * @return
	 */
	private boolean addChDataKNETCNT(byte[] chdata,winFileDataBase db) {
		
		K_NET_CNT knet = new K_NET_CNT(chdata);
		if(knet.purse()!=0) return false;
		if(knet.Data==null) return false;
		
		wintime sm = new wintime(knet.StartTime);
		wintime tm = new wintime(sm);
		tm.inc_sec(59);

		if(LatestTime.year()==0 || tm.getValueS()>LatestTime.getValueS()){
			LatestTime = new wintime(tm);
		}
		if(OldestTime.year()==0 || sm.getValueS()<OldestTime.getValueS()){
			OldestTime = new wintime(sm);
		}
		
		Iterator<Integer> ite = knet.ChannelID.iterator();
		while(ite.hasNext()){
			int ch = ite.next();
			int samplerate = knet.SampleRate.get(ch);
			int[] data = knet.Data.get(ch);
			ChannelData[] cd = ChannelData.makeDataGeneric(ch, data, new wintime(sm), samplerate);
			ChannelDataCollection dcol = getChannelDataCollection(ch);
		
			if(dcol == null){
				dcol = new ChannelDataCollection();
			}
			
			addChannel(ch);
			if(db != null) {
				db.addch(ch);
			}

			for(int i=0;i<cd.length;i++){
				dcol.addChannelData(cd[i]);
				putData(ch,dcol);
				
				buildChData1(ch,cd[i]);
				
				if(cd[i].getTime().sec()==59){
					buildChData60(ch,cd[i].getTime());
				}
				// 中断できるように追加したが、うまく中断できないかも
				if(fileReadingProgressDialog.isCanceled()){
					if(DataSetter!=null) DataSetter.afterAddBlockData(chdata);
					return false;
				}
			}
			if(db != null)  db.set(sm, tm);
			

			
			//if(Listener!=null) Listener.OnAddKNETChannelInfo(winDataBaseChange.EVENT_KNET_INFO_ADD,ch,knet);
		}
		return true;
	}
	
	/**
	 * CHデータを追加 
	 * @param chdata
	 * 　生のデータ
	 * @param db
	 * winファイルデータベース(ファイルをリロードするためのデータベース）
	 * @return
	 */
	public synchronized boolean addChData(byte[] chdata,winFileDataBase db){
		
		boolean ret = true;
//		wintime sm=new wintime();
//		wintime tm=new wintime();
//		boolean first=true;
		if(DataSetter!=null) DataSetter.beforeAddBlockData(chdata);
		if(true == memoryManagement(chdata.length*4/1024)){ // メモリが確保できたら
			int type = winchk.getFileFormat(chdata, chdata.length); // A0=0 A5=5
			int max = chdata.length;
			fileReadingProgressDialog.getProgressBar2().setMaximum(max);
			fileReadingProgressDialog.getProgressBar2().setMinimum(0);
			if(type == winchk.WIN_FORMAT_A0){
				ret = addChDataA0(chdata,db);
			}
			else if(type == winchk.WIN_FORMAT_A5){
				ret = addChDataA5(chdata,db);
			}
			else if(type == winchk.TXT_FORMAT_KNET){
				ret = addChDataKNETascii(chdata,db);
			}
			else if(type == winchk.K_NET_CNTFILE){
				ret = addChDataKNETCNT(chdata,db);
			}
			else if(type == winchk.WIN_FORMAT_A89){
			}
			else{
				System.err.println("Error format A0/A5/K-NET ascii!");
			}
		}
		else{
			System.out.println(String.format("Read aborted. %,dKB ",chdata.length*4/1024));
			ret = false;
		}
		if(DataSetter!=null) DataSetter.afterAddBlockData(chdata);
		return ret;
	}
	/**
	 * ファイル名を指定してファイルデータベースを取得する
	 * @param file
	 * @return
	 */
	private winFileDataBase getWinFileData(String file){
		Iterator<winFileDataBase> ite = FileDataBase.iterator();
		winFileDataBase db=null;
		while(ite.hasNext()){
			winFileDataBase db2 = ite.next();
			if(db2.getFileName().compareToIgnoreCase(file)==0){
				db = db2;
				break;
			}
		}
		if(db==null) db = new winFileDataBase();
		return db;
	}
	/**
	 * ｃｈとwintimeを指定してファイルデータベースを得る
	 * @param ch
	 * @param tm
	 * @return
	 * 存在しない場合はnullを返す
	 */
	private winFileDataBase getWinFileData(int ch,wintime tm){
		Iterator<winFileDataBase> ite = FileDataBase.iterator();
		winFileDataBase db=null;
		while(ite.hasNext()){
			winFileDataBase db2 = ite.next();
			if(db2.isContainCh(ch)){
				if(db2.isContainTime(tm)){
					db = db2;
					break;
				}
			}
		}
		return db;
	}
	/**
	 * ファイルから全部読み出す
	 * @param file
	 * @return
	 * 失敗するとnullを返す
	 */
	private byte[] readFromFile(String file){
		byte[] buf = null;
		try{
			FileInputStream fr = new FileInputStream(file);
			int len = fr.available();
			buf = new byte[len];
			fr.read(buf);
			fr.close();
		}
		catch(IOException e){
			e.printStackTrace();
		}
		return buf;
	}
	/**
	 * ファイルからデータを追加する
	 * @param file
	 * @return
	 */
	public synchronized boolean addChData(String file){
		boolean ret = true;
		fileReadingProgressDialog.setText1(String.format("%s",file));

		winFileDataBase db = getWinFileData(file);
		if(db.Oldest==null){ // 最初だけチェックする
			winchk chk = new winchk();
			if(chk.check(file,0,false) != 0){
				System.err.println("Reading Aborted!!!\nError file="+file);
				return false;
			}
			chk.getWinFormat(); // 0:A0 5:A5
		}
//		try{
//			FileInputStream fr = new FileInputStream(file);
//			int len = fr.available();
//			byte[] buf = new byte[len];
//			fr.read(buf);
//			fr.close();
			
			byte[] buf = readFromFile(file);
			if(buf == null) return false;
			db.set(file, true);
			if(ret = addChData(buf,db)){
				FileDataBase.add(db);
			}
//		}catch(IOException e){
//			e.printStackTrace();
//		}
//		fileReadingProgressDialog.setVisible(false);
		return ret;
	}
	
	// ------------------------------------------
	// ファイルツリーセットからデータを追加する
	public synchronized boolean addChData(TreeSet<String> ts){
		boolean ret = true;
		Iterator<String> ite = ts.iterator();
		fileReadingProgressDialog.getProgressBar1().setMaximum(ts.size());
		fileReadingProgressDialog.getProgressBar1().setMinimum(0);
		fileReadingProgressDialog.getProgressBarMemory().setMaximum(100);
		fileReadingProgressDialog.getProgressBarMemory().setMinimum(0);
		fileReadingProgressDialog.getProgressBarMemory().setValue(0);
		int count=0;
		while(ite.hasNext()){
			fileReadingProgressDialog.getProgressBar1().setValue(++count);
			String fs = ite.next();
	  		if(!addChData(fs)){
	  			ret = false;
	  			// TODO ファイルツリーセットからデータを追加するときにエラーが発生した。さてどうするか。
	  			break; // ブレークすることにする。
	  		}
	  		
	    }
	    return ret;
	}
	// 別スレッドを作成してチャネルデータを追加する
	public  void addChDataTh(Object ts){
		addChannelThread thread = new addChannelThread();
		thread.data = ts;
		
		if(DataSetter!=null){
			Point pt = DataSetter.getLocationOnScreen();
			pt.x  += 100;
			pt.y += 100;
			fileReadingProgressDialog.setLocation(pt);
		}
		
		new Thread(thread).start();
		// 中断できるようにプログレスダイアログを表示
		//System.out.println("Thread show");
		fileReadingProgressDialog.setVisible(true);
		
	}	
	// スレッドを作成してチャネルデータを追加
	class addChannelThread extends Thread{
		public Object data = null;
		public WinDataBaseCallBack callback = null;
		//@SuppressWarnings("unchecked")
		public void run(){
			fileReadingProgressDialog.reset();
			if(data instanceof TreeSet<?>){
				addChData((TreeSet<String>)data);
			}
			else if(data instanceof String){
				addChData((String)data);
			}
			fileReadingProgressDialog.setVisible(false);
		}
	}
	// ------------------------------------------
	// UDPパケットからデータを追加する
	// ------------------------------------------
	// UDPパケットからデータを追加
	public synchronized void addChData(DatagramPacket pac)
	{
		// win A0/actProtocol
		byte[] pbuf = pac.getData();
		addUdpChData(pbuf);
	}
	public synchronized void addUdpChData(byte[] pacdata){
		int len     = pacdata.length;
		if(ActProtocolFrame.isActHeader(pacdata, 0) ){
			// actprotcol
			if(ActProtocolFrame.getDataType(pacdata, 0)==0x00a0){ // win A0 format 0x00a0
				//len -= (18+2+3-4);
				len -= (18+2+3+2-4);
				byte[] buf = new byte[len];
				
				buf[0] = 0;
				buf[1] = (byte)(len / 0x10000);
				len -= 0x10000*buf[1];
				buf[2] = (byte)(len / 0x100);
				len -= 0x100*buf[2];
				buf[3] = (byte)(len);
				
				//System.out.println(String.format("ACT Recv %dbyte",buf.length-6));
				System.arraycopy(pacdata, 18+5, buf, 4, buf.length-4);
				addChData(buf);
				
			}
		}
		else{
//			// win A0 protocol
//			byte[] buf = new byte[len-1];
//			// ファイルと同じ構造にする為に、最初の４バイトを全体の長さ（自身を含む）に書き換え
//			len -= 1;
//			buf[0] = 0;
//			buf[1] = (byte)(len / 0x10000);
//			len -= 0x10000*buf[1];
//			buf[2] = (byte)(len / 0x100);
//			len -= 0x100*buf[2];
//			buf[3] = (byte)(len);
//			
//			//System.out.println(String.format("WinA0 Recv %dbyte",buf.length-4));
//			System.arraycopy(pacdata, 5, buf, 4, buf.length-4);
//			//for(int i=4;i<buf.length;i++) buf[i] = pacdata[i+1];
//			addChData(buf);
			int lanlen = pacdata.length;
			int paclenindex = 3;
			//int packlen = unsigned_byte.convShort(pacdata[paclenindex]) * 0x100 + unsigned_byte.convShort(pacdata[paclenindex+1]);
			//int framelen = packlen  + 3;	// control + sub-control + A0 + paclen;
			lanlen -= 3;
			// 1つのLANフレームに複数のWINパケット（パケット以降）が含まれるケース
			//packlen += 2;
			while(lanlen>0){
				int packlen = unsigned_byte.convShort(pacdata[paclenindex]) * 0x100 + unsigned_byte.convShort(pacdata[paclenindex+1]);
				lanlen -= (packlen);
				packlen += 2;
				int size = packlen;
				byte[] buf = new byte[size];
				buf[0] = 0;
				buf[1] = (byte)(size / 0x10000);
				size -= 0x10000*buf[1];
				buf[2] = (byte)(size / 0x100);
				size -= 0x100*buf[2];
				buf[3] = (byte)(size);
				
				//System.out.println(String.format("WinA0 Recv %dbyte",buf.length-4));
				System.arraycopy(pacdata, paclenindex+2, buf, 4, packlen-4);
				//for(int i=4;i<buf.length;i++) buf[i] = pacdata[i+1];
				addChData(buf);
				
				
				paclenindex += (packlen-2);
			}
			// System.out.println("LAN Remain " + lanlen);
		}
		
	}
	private int select_re_sampleMode(){
		String selectvalues[] = {"abort","insert zero","zeroth hold"};
		String obj = (String)JOptionPane.showInputDialog(null,
		                                     "Found different sample rate. abort or re-sample?",
		                                     "Export",
		                                     JOptionPane.QUESTION_MESSAGE,
		                                     null,
		                                     selectvalues,
		                                     selectvalues[2]);
		if(obj==null) return -1;
		for(int i=0;i<selectvalues.length;i++){
			if(obj.compareTo(selectvalues[i])==0){
				return i;
			}
		}
		return -1;
	}
	/*
	 * 指定のサンプルレートにリサンプリングする
	 * 　整数倍でない場合はnullを返す
	 */
	private int[] re_sample(int newrate,int[] dat,boolean zerothHold){
		int [] ret = new int[newrate];
		int oldrate=dat.length;
		if(oldrate > newrate){	/// down sample
			if((oldrate % newrate) != 0) return null;
			int j=0;
			for(int i=0;i<newrate;i++){
				ret[i] = dat[j];
				j += (oldrate/newrate);
			}
		}
		else{	// up sample
			if((newrate % oldrate) != 0) return null;
			int j=0;
			int step=newrate/oldrate;
			for(int i=0;i<newrate;i++){
				if(j % step == 0){
					ret[i] = dat[j++];
				}
				else{
					if(zerothHold) 	ret[i]=ret[i-1];	// 0次ホールドを挿入
					else 			ret[i]=0;			// 0を挿入
				}
			}
		}
		return ret;
	}
	/**
	 * export用にデータのリストを取得する
	 * @param hs
	 * 対象のｃｈリスト
	 * @param tm
	 * 時刻（１秒前）
	 * @param tm2
	 * 時刻
	 * @param hdata
	 * データ（１秒前）
	 * @param hdata2
	 * データ
	 * @return
	 * hdata,hdata2に返す
	 */
	static int ReSmpMode = -1;
	private int getExportDataArrayList(ArrayList<Integer> hs,wintime tm,wintime tm2,ArrayList<int[]> hdata,ArrayList<int[]> hdata2){
		int errsamplerate=0;
		int sampletime=0;
		boolean error = false;
		Iterator<Integer> ite = hs.iterator();
		boolean zerothHold =true;
		while(ite.hasNext()){
			int ch=ite.next();
			ChannelData cd = getDataFromTime(ch,tm);
			if(cd==null) 	hdata.add(null);	// ない場合もある
			else{
				int [] data = cd.getData();
				if(data==null){
					System.err.format("%s : Null Data!!! maybe swapped out", tm.fmt());
					break;
				}
				if(data.length==0){
					break;
				}
				// すべて同じサンプルレートでないといけない →リサンプルする
				if(sampletime==0) sampletime = data.length;
				else {
					if(sampletime != data.length) {
						//System.out.println(String.format("Different samplerate %d %d", sampletime,data.length));
						if(ReSmpMode<0){	// ユーザに尋ねる
							ReSmpMode = select_re_sampleMode();
							if(ReSmpMode == 1) {		
								zerothHold = false;
							}
							else if(ReSmpMode == 2){
								zerothHold = true;
							}
						}
						int[] tmp=re_sample(sampletime,data,zerothHold);
						if(tmp==null || ReSmpMode == 0){
							errsamplerate = data.length;
							error = true;
							break;
						}
						else{
							data = tmp;
						}
					}
				}
				// データのみをリストにする
				hdata.add(data);
			}
			ChannelData cd2 = getDataFromTime(ch,tm2);
			if(cd2==null) 	hdata2.add(null);	// ない場合もある
			else{
				int [] data = cd2.getData();
				// すべて同じサンプルレートでないといけない
				if(sampletime==0) sampletime = data.length;
				else {
					if(sampletime != data.length) {
						int[] tmp=re_sample(sampletime,data,zerothHold);
						if(tmp==null){
							errsamplerate = data.length;
							error = true;
							break;
						}
						else{
							data = tmp;
						}
					}
				}
				// データのみをリストにする
				hdata2.add(data);
			}
		}
		if(error){
			JOptionPane.showMessageDialog(null, String.format("Export aborted - different sample rate %d %d", sampletime,errsamplerate));
			sampletime =  -1;
		}
		else if(sampletime==0){
			//JOptionPane.showMessageDialog(null, String.format("Export aborted - data not found.(Data may be swapped out because of less memory or export range is too large"));
		}
		return sampletime;
	}
	
	/**
	 * 時間を指定してChannelData を得る
	 * @param ch
	 *   int チャネル
	 * @param tm
	 * 　　wintime 時刻
	 * @return
	 *  ChannelData / null
	 */
	private ChannelData getDataFromTime(int ch,wintime tm){
		ChannelDataCollection dcol = getChannelDataCollection(ch);
		if(dcol==null) return null; //ｃｈ自体が無い
		TreeMap<Integer,ChannelData> cdlist = dcol.getChannelData();
		int tml = tm.getValueS();
		ChannelData cd = cdlist.get(tml);
		if(cd == null){ // purgeされている可能性がある
			ChannelOutlineDataCollection dcol1 = getChannelDataCollection1(ch);
			if(dcol1==null) return null;

//			TreeMap<Integer, ChannelOutlineData> cdlist1 = dcol1.getChannelData();
//			ChannelOutlineData cd1 = cdlist1.get(tml);
//			if(cd1==null) return null;
			
			winFileDataBase db = getWinFileData(ch,tm);
			if(db==null){
				return null;
			}
			cd = getDataFromTime(db.getFileName(),db.getFileType(),ch,tm);
			// Exportでパージされていると、何度もファイルを読むことになるので、ここで画面に読み込んでしまえばよいのではないか。
			addChData(db.getFileName());// TODO ここは副作用があるかも
		}
		return cd;
	}
	private ChannelData getDataFromTime(String file,int filetype,int ch,wintime tm){
		ChannelData cd = null;
		try{
			FileInputStream fr = new FileInputStream(file);
			
			switch(filetype){
			case 0:	// win A0
				cd = getDataFromTimeA0(fr,ch,tm);
				break;
			case 5: // win A5
				cd = getDataFromTimeA5(fr,ch,tm);
				break;
			}
			fr.close();
		}
		catch(IOException e){
			e.printStackTrace();
		}	
		return cd;
	}
	/**
	 * A0形式のファイルからの指定ブロックのみのリード
	 * @param fr
	 * @param wch
	 * @param wtm
	 * @return
	 */
	private ChannelData getDataFromTimeA0(FileInputStream fr,int wch,wintime wtm){
		int max;
		try {
			max = fr.available();
		} catch (IOException e1) {
			e1.printStackTrace();
			return null;
		}
		wintime tm=new wintime();
		for(int offset=0;offset<max;){
			SecondBlock blk;
			try {
				blk = new SecondBlock(fr,0);
			} catch (IOException e) {
				e.printStackTrace();
				break;
			}
			tm = blk.getWinTime();
			if(tm==null){
				return null;
			}
			Iterator<ChannelData> ite = blk.getChannelDataList().iterator();
			while(ite.hasNext()){
				ChannelData d = ite.next();
				int ch = d.getChannelName();
				if(ch==wch){
					if(tm.compare(wtm)==0){
						return d;
					}
				}
			}
			offset += blk.length();
		}
		return null;
	}
	private ChannelData getDataFromTimeA5(FileInputStream fr,int wch,wintime wtm){
		ArrayList<SecondBlock> blklist = new ArrayList<SecondBlock>();
		
		wintime tm=new wintime();
//		boolean first=true;
		for(int offset=0;;){
			SecondBlock blk;
			try {
				blk = new SecondBlock(fr,offset,5);
			} catch (IOException e) {
			
				e.printStackTrace();
				break;
			}
			offset += blk.length();
			tm = blk.getWinTime();
			blklist.add(blk);
			if(tm.msec() == 0){
			}
			else if(tm.msec() == 900){
				// ｃｈの数を求める
				TreeSet<Integer> chtree = new TreeSet<Integer>();
				for(int i=0;i<blklist.size();i++){
					blk = blklist.get(i);
					Iterator<ChannelData> ite = blk.getChannelDataList().iterator();
					while(ite.hasNext()){
						ChannelData d = ite.next();
						int ch = d.getChannelName();
						chtree.add(ch);
					}
				}
				// データをｃｈ、時間ばらばらにする
				ChannelData[][] cd = new ChannelData[chtree.size()][10];
				for(int i=0;i<blklist.size();i++){
					blk = blklist.get(i);
					Iterator<ChannelData> ite = blk.getChannelDataList().iterator();
					while(ite.hasNext()){
						ChannelData d = ite.next();
						int ch = d.getChannelName();
						int index=0;
						Iterator<Integer> ite2  = chtree.iterator();
						while(ite2.hasNext()){
							int ch2 = ite2.next();
							if(ch2==ch) break;
							index++;
						}
						cd[index][i]=d;
					}
				}
				// channel毎にループ
				for(int ch=0;ch<chtree.size();ch++){
					ArrayList<ChannelData> ad = new ArrayList<ChannelData>();
					for(int j=0;j<10;j++){
						ad.add(cd[ch][j]);
					}
					// １秒分まとめる
					ChannelData d = ChannelData.assembleChannelData(ad);
					// 同じ処理
					addChannel(ch);
					ChannelDataCollection dcol = getChannelDataCollection(ch);
					if(dcol == null){
						dcol = new ChannelDataCollection();
					}
					dcol.addChannelData(d);
					putData(ch,dcol);
					
					buildChData1(ch,d);
					
					// ６０秒分がそろった可能性がある
					if(d.getTime().sec()==59){
						buildChData60(ch,tm);
					}
					
				}
				blklist.clear();
			}
		}
		return null;
	}
	// -------------------------------------------------------------------------------------------
	// -------
	/**
	 * Binaryにexportする. 指定範囲は秒に丸められる。
	 * @param st
	 *  wintime 開始時刻 秒単位
	 * @param en
	 *  wintime 終了時刻 秒単位
	 * @param file
	 *  File　出力ファイル
	 * @param hs
	 *  ArrayList<Integer> ｃｈのリスト
	 *  @param timeshift
	 *  時刻をシフト　単位は秒
	 * @return
	 *  成功時出力した行数　 エラー時 <0 
	 */
	public int doExportBin(wintime st,wintime en,File file,ArrayList<Integer> hs,double timeshift,boolean showresult,ProgressDialog dlg,int decimation,int decimationmode,
			boolean littleendian){
		
		int retcounter = 0;
		boolean error = false;
	// --------------------------------
		// ファイルをオープン
		try{
			String fname = file.getCanonicalPath();
			FileOutputStream fw=new FileOutputStream(fname);
			BufferedOutputStream bw  =new BufferedOutputStream(fw);
			
			//fileReadingProgressDialog.setVisible(true);
			/////////////////////////////////
			wintime tm = new wintime(st);// 1秒前のデータ
			tm.dec_sec();
			wintime tm2 = new wintime(st);//　これが該当秒のデータ 
			int latesttime = en.getValueS();
			
			// 秒と秒以下に分ける
			int deltasec = (int)(timeshift);
			double deltasub  = (timeshift-(double)deltasec); 
			if(deltasub > 0.0){
				latesttime++;
			}
			else if(deltasub<0){
				deltasec--;
				latesttime++;
				deltasub = deltasub+1;
			}
			
			wintime tmsft = new wintime(st);
			tmsft.inc_sec(deltasec); // 表示用のあたらしい時刻（秒部分）
			//wintime tmsftorg = new wintime(tmsft);
			
			if(dlg!=null){
				dlg.setText1(file.getAbsolutePath());
				dlg.getProgressBar1().setMinimum(tm2.getValueS());
				dlg.getProgressBar1().setMaximum(latesttime);
				dlg.getProgressBar2().setMinimum(0);
			}
			
			int sampletime=0;
		//	String errorinfo = new String();
			int cursec;
			int datacounter=0;
			wintime startTime=null;
			wintime startTimeSft=null;
			wintime endTime=new wintime(tm2);
			wintime endTimeSft=new wintime(tmsft);
			boolean dataEmpty=true;
			
			
			// 時間順にループ
			ArrayList<int[]> hdata = new ArrayList<int[]>();
			ArrayList<int[]> hdata2 = new ArrayList<int[]>();

			sampletime = getExportDataArrayList(hs,tm,tm2,hdata,hdata2);
			if(sampletime<0){
				error = true;
			}
			else{
				int maxch = hdata.size();
				int [][] dataallch = new int[maxch][decimation];
				
				while((cursec = tm2.getValueS()) <= latesttime){
					hdata.clear();
					hdata2.clear();
					sampletime = getExportDataArrayList(hs,tm,tm2,hdata,hdata2);
					if(sampletime<0){
						error = true;
						break;
					}
					else if(sampletime !=0){
						dataEmpty=false;
						if(startTime==null){
							startTime = new wintime(tm2);
						}
						if(startTimeSft==null){
							startTimeSft = new wintime(tmsft);
						}
						endTime=new wintime(tm2);
						endTimeSft=new wintime(tmsft);
						if(dlg!=null){
							if(dlg.isCanceled()){
								bw.flush();
								//fw.flush();
								error = true;
								break;
							}
							dlg.setText2(tm2.fmt()+" : "+retcounter + "line");
							dlg.getProgressBar1().setValue(cursec);
						}
						// 一秒分を単位にループ
						if(retcounter % 1000==0){
							memoryManagement(100);
						}
		
						
						double ticktime = 1/(double)sampletime;
					    // 読み飛ばすサンプル数
						int skipsample = (int)((deltasub / ticktime) + 0.5);
						if(dlg!=null){
							dlg.getProgressBar2().setMaximum(sampletime);
						}
						for(int i=0;i<sampletime;i++){
							if(decimationmode == 0){// 単純な間引き
								retcounter = exportBinSimple(dlg, decimation, retcounter, bw,//fw,
									tmsft, sampletime, datacounter, hdata, hdata2,
									ticktime, skipsample, i, littleendian);
							}
							else if(decimationmode == 1){// メジアン
								retcounter = exportBinMedianOrMean(dlg, decimation, retcounter, bw,//fw,
										tmsft, sampletime, datacounter, hdata, hdata2,
										ticktime, skipsample, i,dataallch , 1, littleendian);
								
							}
							else if(decimationmode == 2){// 算術平均
								retcounter = exportBinMedianOrMean(dlg, decimation, retcounter, bw,//fw,
										tmsft, sampletime, datacounter, hdata, hdata2,
										ticktime, skipsample, i,dataallch , 2, littleendian);
								
							}
							datacounter++;
						}
					}
					tm.inc_sec();
					tm2.inc_sec();
					tmsft.inc_sec();
				}
			}
			bw.close();
			fw.close();
			if(showresult){
				if(!error){
					String msg = "Export Binary ";
					if(dataEmpty){
						msg += "\n no data";
					}
					else{
						msg += String.format("success\nFile=%s\nOutput  = %,d data\nShift   = %,g sec\n", file.getAbsolutePath(),retcounter,timeshift);
						//msg += "Original data = " + st.fmt()+ " - "+en.fmt()+"\n";
						//msg += "Shifted data  = " +tmsftorg.fmt() + " - " + tmsft.fmt();
						msg += "Original data = " + startTime.fmt()    + " - " + endTime.fmt()+"\n";
						msg += "Shifted data  = " + startTimeSft.fmt() + " - " + endTimeSft.fmt();
					}
					JOptionPane.showMessageDialog(null,msg);
				}
				else{
					String msg = String.format("Export Binary error or aborted\nFile=%s", file.getAbsolutePath());
					JOptionPane.showMessageDialog(null,msg);
				}
			}
			
		} catch (IOException e){
			e.printStackTrace();
			error = true;
			retcounter=-1;
		}
		return retcounter;
	}
	private boolean exportBinOutputFile(/*FileOutputStream*/ BufferedOutputStream fw,int dat,boolean littleendian){
		int[] tmp = { 0,0,0,0 };
		int size = 4;
		try {
			if(littleendian){
				for(int i=0;i<size;i++){
					tmp[i] = dat & 0xff;
					dat >>= 8;
				}
			}
			else{
				for(int i=0;i<size;i++){
					tmp[size - i -1] = dat & 0xff;
					dat >>= 8;
				}
			}
			for(int i=0;i<size;i++){
				fw.write(tmp[i]);
			}
		} catch (IOException e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}
	/**
	 * exportCSVの単純間引きモード
	 * @param dlg
	 * @param decimation
	 * @param retcounter
	 * @param fw
	 * @param tmsft
	 * @param sampletime
	 * @param datacounter
	 * @param hdata
	 * @param hdata2
	 * @param ticktime
	 * @param skipsample
	 * @param i
	 * @return
	 */
	private int exportBinSimple(ProgressDialog dlg, int decimation,
			int retcounter, /*FileOutputStream*/BufferedOutputStream fw, wintime tmsft, int sampletime,
			int datacounter, ArrayList<int[]> hdata, ArrayList<int[]> hdata2,
			double ticktime, int skipsample, int i, boolean littleendian) {
		if(datacounter % decimation == 0){ // decimationmode = simpleのみ
			if(dlg!=null){
				dlg.getProgressBar2().setValue(i);
			}
			Iterator<int []> datite=null;
			int index ;
			if((i)<skipsample){
				datite = hdata.iterator();
				index = i+(sampletime - skipsample);
			}
			else{
				datite = hdata2.iterator();
				index = i-(skipsample);
			}
			while(datite.hasNext()){
				int [] data = datite.next();
				if(data == null) break;
				exportBinOutputFile(fw,data[index],littleendian);
				retcounter++;
			}
		}
		return retcounter;
	}
	/**
	 * exportBinのメジアンモード 
	 * 　　　時刻 n～n+mのメジアンを時刻nのデータにする。
	 *   データの数m+1が偶数のときは中心の２つのデータの平均とする。
	 * @param dlg
	 * @param decimation
	 * @param retcounter
	 * @param fw
	 * @param tmsft
	 * @param sampletime
	 * @param datacounter
	 * @param hdata
	 * @param hdata2
	 * @param ticktime
	 * @param skipsample
	 * @param i
	 * @param dataallch [ch][data lenght of decimation]
	 * @param mode 1=median ,2=arithmetic mean;
	 * @return
	 */
	private int exportBinMedianOrMean(ProgressDialog dlg, int decimation,
			int retcounter, /*FileOutputStream*/BufferedOutputStream fw, wintime tmsft, int sampletime,
			int datacounter, ArrayList<int[]> hdata, ArrayList<int[]> hdata2,
			double ticktime, int skipsample, int i, int[][] dataallch, int mode,boolean littleendian) {
		if(datacounter % decimation == (decimation -1 )){ // decimationmode = simpleのみ
			if(dlg!=null){
				dlg.getProgressBar2().setValue(i);
			}
			if(exportDataChData(hdata,hdata2,skipsample,i, sampletime, dataallch,decimation)){
				int maxch=hdata.size();
				Integer[] dd;
				if(mode == 1) 	dd= getMedian(dataallch,maxch,decimation);
				else 			dd= getArithmeticMean(dataallch,maxch,decimation);
				for(int ch=0;ch<maxch;ch++){
					exportBinOutputFile(fw,dd[ch],littleendian);
					retcounter++;
				}
			}
		}
		else{
			exportDataChData(hdata,hdata2,skipsample,i, sampletime, dataallch,decimation);
		}
		return retcounter;
	}

	// -------
	// -------------------------------------------------------------------------------------------
	// -------------------------------------------------------------------------------------------
	// -------
	/**
	 * CHプロパティから物理換算係数,物理量を得る。
	 * @param channelProp
	 * @return
	 */
	private double[] getFactors(ArrayList<ChannelDataDisplayProperty> channelProp){
		if(channelProp==null) return null;
		int sz = channelProp.size();
		double[] ret = new double[sz];
		Iterator<ChannelDataDisplayProperty> ite = channelProp.iterator();
		int i=0;
		while(ite.hasNext()){
			ChannelDataDisplayProperty cp = ite.next();
			ret[i++] = cp.unitFactor;
		}
		return ret;
		
	}
	/**
	 * CHプロパティから物理換算係数,物理量を得る。
	 * @param channelProp
	 * @return
	 */
	private String[] getUnits(ArrayList<ChannelDataDisplayProperty> channelProp){
		if(channelProp==null) return null;
		int sz = channelProp.size();
		String[] ret = new String[sz];
		Iterator<ChannelDataDisplayProperty> ite = channelProp.iterator();
		int i=0;
		while(ite.hasNext()){
			ChannelDataDisplayProperty cp = ite.next();
			ret[i++] = cp.unitString;
		}
		return ret;
		
	}
	
	/**
	 * CSVにexportする. 指定範囲は秒に丸められる。
	 * @param st
	 *  wintime 開始時刻 秒単位
	 * @param en
	 *  wintime 終了時刻 秒単位
	 * @param separator 
	 *  String セパレータ文字列
	 * @param file
	 *  File　出力ファイル
	 * @param hs
	 *  ArrayList<Integer> ｃｈのリスト
	 * @param header
	 *  boolean ヘッダをつけるかどうか
	 * @param time
	 *  時刻をつけるかどうか
	 * @param millseccomma
	 *  ミリ秒をカンマにするかどうか。（ピリオドでなくて）
	 *  @param timeshift
	 *  時刻をシフト　単位は秒
	 * @return
	 *  成功時出力した行数　 エラー時 <0 
	 */
	public int doExportCSV(wintime st,wintime en,String separator,File file,ArrayList<Integer> hs,boolean header,boolean time,boolean millseccomma,double timeshift,
			boolean showresult,ProgressDialog dlg,int decimation,int decimationmode,ArrayList<ChannelDataDisplayProperty> fc){
		
		double[] factor = getFactors(fc);
		String[] unit   = getUnits(fc);
		
		int retcounter = 0;
		boolean error = false;
	// --------------------------------
		// ファイルをオープン
		FileWriter fw2=null;
		try{
			fw2 = new FileWriter(file);
			BufferedWriter bw = new BufferedWriter(fw2,1000000);
			PrintWriter fw = new PrintWriter(bw);
			String mes = new String("");
			if(header){
				ArrayList<String> strarry = new ArrayList<String>();
				if(time) strarry.add("Time");
				if(millseccomma) strarry.add(" ms ");
				Iterator<Integer> ite = hs.iterator();
				while(ite.hasNext()){
					int ch = ite.next();
					strarry.add(String.format(" \"%04x\" ", ch));
				}
				boolean headerfirst=false;
				Iterator<String> strite = strarry.iterator();
				while(strite.hasNext()){
					if(headerfirst) mes += separator;
					String mmm = new String(strite.next());
					mes += mmm;
					headerfirst=true;
				}
				fw.write(mes+"\n");
				
				if(unit!=null){
					writeCSVUnitLine(separator, time, millseccomma, unit, fw);
				}
			
			}
			//fileReadingProgressDialog.setVisible(true);
			/////////////////////////////////
			wintime tm = new wintime(st);// 1秒前のデータ
			tm.dec_sec();
			wintime tm2 = new wintime(st);//　これが該当秒のデータ 
			int latesttime = en.getValueS();
			
			// 秒と秒以下に分ける
			int deltasec = (int)(timeshift);
			double deltasub  = (timeshift-(double)deltasec); 
			if(deltasub > 0.0){
				latesttime++;
			}
			else if(deltasub<0){
				deltasec--;
				latesttime++;
				deltasub = deltasub+1;
			}
			
			wintime tmsft = new wintime(st);
			tmsft.inc_sec(deltasec); // 表示用のあたらしい時刻（秒部分）
			//wintime tmsftorg = new wintime(tmsft);
			
			if(dlg!=null){
				dlg.setText1(file.getAbsolutePath());
				dlg.getProgressBar1().setMinimum(tm2.getValueS());
				dlg.getProgressBar1().setMaximum(latesttime);
				dlg.getProgressBar2().setMinimum(0);
			}
			
			int sampletime=0;
		//	String errorinfo = new String();
			int cursec;
			int datacounter=0;
			wintime startTime=null;
			wintime startTimeSft=null;
			wintime endTime=new wintime(tm2);
			wintime endTimeSft=new wintime(tmsft);
			boolean dataEmpty=true;
			
			
			// 時間順にループ
			ArrayList<int[]> hdata = new ArrayList<int[]>();
			ArrayList<int[]> hdata2 = new ArrayList<int[]>();

			sampletime = getExportDataArrayList(hs,tm,tm2,hdata,hdata2);
			if(sampletime<0){
				error = true;
			}
			else{
				int maxch = hdata.size();
				int [][] dataallch = new int[maxch][decimation];
				
				while((cursec = tm2.getValueS()) <= latesttime){
					hdata.clear();
					hdata2.clear();
					sampletime = getExportDataArrayList(hs,tm,tm2,hdata,hdata2);
					if(sampletime<0){
						error = true;
						break;
					}
					else if(sampletime !=0){
						dataEmpty=false;
						if(startTime==null){
							startTime = new wintime(tm2);
						}
						if(startTimeSft==null){
							startTimeSft = new wintime(tmsft);
						}
						endTime=new wintime(tm2);
						endTimeSft=new wintime(tmsft);
						if(dlg!=null){
							if(dlg.isCanceled()){
								fw.flush();
								error = true;
								break;
							}
							dlg.setText2(tm2.fmt()+" : "+retcounter + "line");
							dlg.getProgressBar1().setValue(cursec);
						}
						// 一秒分を単位にループ
						if(retcounter % 100==0){
							memoryManagement(100);
						}
		
						
						double ticktime = 1/(double)sampletime;
					    // 読み飛ばすサンプル数
						int skipsample = (int)((deltasub / ticktime) + 0.5);
						if(dlg!=null){
							dlg.getProgressBar2().setMaximum(sampletime);
						}
						for(int i=0;i<sampletime;i++){
							if(decimationmode == 0){// 単純な間引き
								retcounter = doExportCSVSecSimple(separator, time,
									millseccomma, dlg, decimation, retcounter, fw,
									tmsft, sampletime, datacounter, hdata, hdata2,
									ticktime, skipsample, i,factor);
							}
							else if(decimationmode == 1){// メジアン
								// TODO 製作中
								retcounter = doExportCSVSecMedianOrMean(separator, time,
										millseccomma, dlg, decimation, retcounter, fw,
										tmsft, sampletime, datacounter, hdata, hdata2,
										ticktime, skipsample, i,dataallch , 1,factor);
								
							}
							else if(decimationmode == 2){// 算術平均
								// TODO 製作中
								retcounter = doExportCSVSecMedianOrMean(separator, time,
										millseccomma, dlg, decimation, retcounter, fw,
										tmsft, sampletime, datacounter, hdata, hdata2,
										ticktime, skipsample, i,dataallch , 2,factor);
								
							}
							datacounter++;
						}
					}
					tm.inc_sec();
					tm2.inc_sec();
					tmsft.inc_sec();
				}
			}
			fw.close();
			if(showresult){
				if(!error){
					String msg = "Export CSV ";
					if(dataEmpty){
						msg += "\n no data";
					}
					else{
						msg += String.format("success\nFile=%s\nOutput  = %,d lines\nShift   = %,g sec\n", file.getAbsolutePath(),retcounter,timeshift);
						//msg += "Original data = " + st.fmt()+ " - "+en.fmt()+"\n";
						//msg += "Shifted data  = " +tmsftorg.fmt() + " - " + tmsft.fmt();
						msg += "Original data = " + startTime.fmt()    + " - " + endTime.fmt()+"\n";
						msg += "Shifted data  = " + startTimeSft.fmt() + " - " + endTimeSft.fmt();
					}
					JOptionPane.showMessageDialog(null,msg);
				}
				else{
					String msg = String.format("Export CSV error or aborted\nFile=%s", file.getAbsolutePath());
					JOptionPane.showMessageDialog(null,msg);
				}
			}
			
		} catch (IOException e){
			e.printStackTrace();
			error = true;
			retcounter=-1;
		}
		return retcounter;
	}
	/**
	 * CSVファイルの物理単位での出力時の単位を出力する。
	 * 
	 * @param separator　セパレータ文字列
	 * @param time　時間を出力するかどうか
	 * @param millseccomma　msecを出力するかどうか
	 * @param unit　物理単位の文字列の配列
	 * @param fw　出力先
	 */
	private void writeCSVUnitLine(String separator, boolean time,
			boolean millseccomma, String[] unit, PrintWriter fw) {
		String mes;
		mes = "";
		ArrayList<String> strarry2 = new ArrayList<String>();
		if(time) strarry2.add(" ");
		if(millseccomma) strarry2.add("  ");
		
		for(int ii=0;ii<unit.length;ii++){
			strarry2.add(unit[ii]);
		}
		boolean headerfirst2=false;
		Iterator<String> strite2 = strarry2.iterator();
		while(strite2.hasNext()){
			if(headerfirst2) mes += separator;
			String mmm = new String(strite2.next());
			mes += mmm;
			headerfirst2=true;
		}
		fw.write(mes+"\n");
	}
	/**
	 * exportCSVの単純間引きモード
	 * @param separator
	 * @param time
	 * @param millseccomma
	 * @param dlg
	 * @param decimation
	 * @param retcounter
	 * @param fw
	 * @param tmsft
	 * @param sampletime
	 * @param datacounter
	 * @param hdata
	 * @param hdata2
	 * @param ticktime
	 * @param skipsample
	 * @param i
	 * @return
	 */
	private int doExportCSVSecSimple(String separator, boolean time,
			boolean millseccomma, ProgressDialog dlg, int decimation,
			int retcounter, PrintWriter fw, wintime tmsft, int sampletime,
			int datacounter, ArrayList<int[]> hdata, ArrayList<int[]> hdata2,
			double ticktime, int skipsample, int i,double[] factor) {
		
		
		String mes;
		if(datacounter % decimation == 0){ // decimationmode = simpleのみ
			if(dlg!=null){
				dlg.getProgressBar2().setValue(i);
			}
			boolean dataAvailable=false;
			boolean sep = false;
			if(time){
				String comma = new String("");
				if(millseccomma) comma = ",";
				else comma = "."; 
				mes = tmsft.fmt() + comma + String.format("%07.3f",1000*ticktime*i);
				sep = true;
			}
			else mes = "";
			
			if((i)<skipsample){
				Iterator<int []> datite = hdata.iterator();
				int ifc = 0;
				while(datite.hasNext()){
					int [] data = datite.next();
					if(data == null) break;
					if(sep) mes += new String(separator);
					int dt = data[i+(sampletime - skipsample)];
					if(factor==null){
						mes += new String(String.format("%d", dt));
					}
					else{
						mes += new String(String.format("%.5e", dt*factor[ifc++]));
					}
					sep = true;
					dataAvailable=true;
				}
			}
			else{
				Iterator<int []> datite = hdata2.iterator();
				int ifc=0;
				while(datite.hasNext()){
					int [] data = datite.next();
					if(data == null) break;
					if(sep) mes += new String(separator);
					int dt = data[i-(skipsample)];
					if(factor==null){
						mes += new String(String.format("%d", dt));
					}
					else{
						mes += new String(String.format("%.5e", dt*factor[ifc++]));
					}
					sep = true;
					dataAvailable=true;
				}
			}
			//if(datacounter % decimation == 0){ // decimationmode = simpleのみ
			if(dataAvailable){
				fw.write(mes+"\n");
				retcounter++;
				//System.out.println(mes);
				if(retcounter % 1000==0){
					fw.flush();
				}
			}
		}
		return retcounter;
	}
	/**
	 * exportCSVのメジアンモード 
	 * 　　　時刻 n～n+mのメジアンを時刻nのデータにする。
	 *   データの数m+1が偶数のときは中心の２つのデータの平均とする。
	 * @param separator
	 * @param time
	 * @param millseccomma
	 * @param dlg
	 * @param decimation
	 * @param retcounter
	 * @param fw
	 * @param tmsft
	 * @param sampletime
	 * @param datacounter
	 * @param hdata
	 * @param hdata2
	 * @param ticktime
	 * @param skipsample
	 * @param i
	 * @param dataallch [ch][data lenght of decimation]
	 * @param mode 1=median ,2=arithmetic mean;
	 * @return
	 */
	private int doExportCSVSecMedianOrMean(String separator, boolean time,
			boolean millseccomma, ProgressDialog dlg, int decimation,
			int retcounter, PrintWriter fw, wintime tmsft, int sampletime,
			int datacounter, ArrayList<int[]> hdata, ArrayList<int[]> hdata2,
			double ticktime, int skipsample, int i, int[][] dataallch, int mode,
			double[] factor) {
	
		
		String mes;
		if(datacounter % decimation == (decimation -1 )){ // decimationmode = simpleのみ
			if(dlg!=null){
				dlg.getProgressBar2().setValue(i);
			}
			boolean sep = false;
			if(time){
				String comma = new String("");
				if(millseccomma) comma = ",";
				else comma = "."; 
				mes = tmsft.fmt() + comma + String.format("%07.3f",(1000*ticktime*(i/decimation))*(decimation));
				sep = true;
			}
			else mes = "";
			if(exportDataChData(hdata,hdata2,skipsample,i, sampletime, dataallch,decimation)){
				int maxch=hdata.size();
				Integer[] dd;
				if(mode == 1) 	dd= getMedian(dataallch,maxch,decimation);
				else 			dd= getArithmeticMean(dataallch,maxch,decimation);
				int ifc = 0;
				for(int ch=0;ch<maxch;ch++){
					if(sep) mes += new String(separator);
					int dt = dd[ch];
					
					if(factor==null){
						mes += new String(String.format("%d", dt));
					}
					else{
						mes += new String(String.format("%.5e", dt*factor[ifc++]));
					}				
					//mes += new String(String.format("%d", dd[ch]));
					sep = true;
				}
				fw.write(mes+"\n");
				retcounter++;
				if(retcounter % 1000==0){
					fw.flush();
				}
			}
		}
		else{
			exportDataChData(hdata,hdata2,skipsample,i, sampletime, dataallch,decimation);
		}
		return retcounter;
	}
	/**
	 * メジアンを求める
	 * 　　　データ数が偶数の場合は中心の２つの算術平均
	 * @param data
	 * @param maxch
	 * @param datanum
	 * @return
	 */
	private Integer[] getMedian(int[][] data,int maxch,int datanum){
		Integer[] ret = new Integer[maxch];
		for(int ch=0;ch<maxch;ch++){
			TreeSet<Integer> set = new TreeSet<Integer>();
			for(int i=0;i<datanum;i++){
				set.add(data[ch][i]);
			}
			Object[] ar = set.toArray();
			int idx = (ar.length)/2;
			if((ar.length%2)==0){
				Integer sum = (Integer)ar[idx] + (Integer)ar[idx-1];
				// 四捨五入
				if(sum % 2 == 0) ret[ch] = sum/2;
				else			 ret[ch] = sum/2 + 1;
			}
			else{
				ret[ch] = (Integer)ar[idx];
			}
		}
		return ret;
	}
	/**
	 * 算術平均を求める
	 * @param data
	 * @param maxch
	 * @param datanum
	 * @return
	 */
	private Integer[] getArithmeticMean(int[][] data,int maxch,int datanum){
		Integer[] ret = new Integer[maxch];
		for(int ch=0;ch<maxch;ch++){
			float sum=0;
			for(int i=0;i<datanum;i++){
				sum += data[ch][i];
			}
			sum /= datanum;
			ret[ch] = (Integer)Math.round(sum);
		}
		return ret;
	}
	/**
	 * 指定時間の全ｃｈのデータを得る
	 * @param hdata　　１秒前の１秒分のデータ
	 * @param hdata2　　該当秒の１秒分のデータ
	 * @param skipsample　オフセットするインデックス
	 * @param i　インデックス
	 * @param sampletime サンプルレート
	 * @param retdata [ch][index]
 	 * @return FALSE: データがなかった
	 */
	private boolean exportDataChData(ArrayList<int[]> hdata, ArrayList<int[]> hdata2,int skipsample,int i, int sampletime, int[][] retdata,int decimation){
		Iterator<int []> datite=null;
		int index ;
		if((i)<skipsample){
			datite = hdata.iterator();
			index = i+(sampletime - skipsample);
		}
		else{
			datite = hdata2.iterator();
			index = i-(skipsample);	
		}
		int ch=0;
		while(datite.hasNext()){
			int [] data = datite.next();
			if(data == null) return false;
			retdata[ch++][i%decimation] = data[index];
		}
		return true; 
	}
	/**
	 * export CSV,Binaryで中断できるように別スレッド処理としてダイアログをつける
	 * @param mode
	 * @param st
	 * @param en
	 * @param separator
	 * @param file
	 * @param hs
	 * @param header
	 * @param time
	 * @param millseccomma
	 * @param timeshift
	 * @param showresult
	 * @param decimation
	 * @param decimationmode
	 * @return
	 */
	private int exportRunThread(int mode,wintime st,wintime en,String separator,File file,ArrayList<Integer> hs,boolean header,boolean time,boolean millseccomma,
			double timeshift,boolean showresult,int decimation,int decimationmode,boolean littleendian,ArrayList<ChannelDataDisplayProperty> cp){
		
		
		ReSmpMode = -1;
		exportCSVThead thread = new exportCSVThead(st,en,separator,file,hs,header,time,millseccomma,timeshift,showresult,cp);
		thread.mode=mode; 
		//thread.setPriority(Thread.MAX_PRIORITY);
		thread.decimation = decimation;
		thread.decimationmode = decimationmode;
		thread.littleendian = littleendian;
		
		if(DataSetter!=null){
			Point pt = DataSetter.getLocationOnScreen();
			pt.x  += 100;
			pt.y += 100;
			fileReadingProgressDialog.setLocation(pt);
		}
		new Thread(thread).start();
		if(mode==0) fileReadingProgressDialog.setTitle("Writing CSV ...");
		else        fileReadingProgressDialog.setTitle("Writing Binary ...");
		fileReadingProgressDialog.setVisible(true);
	
		return 0;
	}
	/**
	 * CSVにエクスポートする　ＵＩを持つ別スレッドで実行する。
	 * @param st
	 * @param en
	 * @param separator
	 * @param file
	 * @param hs
	 * @param header
	 * @param time
	 * @param millseccomma
	 * @param timeshift
	 * @param showresult
	 * @param decimation
	 * @param decimationmode
	 * @return
	 */
	public int exportCSV(wintime st,wintime en,String separator,File file,ArrayList<Integer> hs,boolean header,boolean time,boolean millseccomma,double timeshift,
			boolean showresult,int decimation,int decimationmode,ArrayList<ChannelDataDisplayProperty>  cp){
		return exportRunThread(0, st,en,separator,file,hs,header, time, millseccomma, timeshift, showresult, decimation, decimationmode,false,cp);
	
	}
	/**
	 * バイナリファイルにエクスポートする 　ＵＩを持つ別スレッドで実行する。
	 * @param st
	 * @param en
	 * @param file
	 * @param hs
	 * @param timeshift
	 * @param showresult
	 * @param decimation
	 * @param decimationmode
	 * @param littleendian
	 * @return
	 */
	public int exportBin(wintime st,wintime en,File file,ArrayList<Integer> hs,double timeshift,boolean showresult,int decimation,int decimationmode,boolean littleendian){
		return exportRunThread(1, st,en,null,file,hs,false, false, false, timeshift, showresult, decimation, decimationmode,littleendian,null);
	
	}
	/* 
	 * CSV,Binaryでエクスポート UIを行うスレッドクラス
	 * 
	 * 
	 */
	class exportCSVThead extends Thread{
		//public TreeSet<String> treeset =null;
		private wintime st = null;
		private wintime en = null;
		private File file = null;
		private ArrayList<Integer> hs = null;
		private double timeshift = 0;
		private String separator;
		private boolean header;
		private boolean time;
		private boolean millseccomma;
		private boolean showresult;
		public int retcounter=0;
		public int decimationmode=0;
		public int decimation=1;
		public int mode = 0; // 0=CSV 1=Binary
		public boolean littleendian;
		public ArrayList<ChannelDataDisplayProperty> Cp = null;
		
		exportCSVThead(wintime wst,wintime wen,String wseparator,File wfile,ArrayList<Integer> whs,boolean wheader,boolean wtime,boolean wmillseccomma,
				double wtimeshift,boolean wshowresult,ArrayList<ChannelDataDisplayProperty> cp){
			st = new wintime(wst);
			en = new wintime(wen);
			String fn = wfile.getAbsolutePath();
			file = new File(fn);
			hs  = new ArrayList<Integer>();
			Iterator<Integer> ite = whs.iterator();
			while(ite.hasNext()){
				hs.add(ite.next());
			}
			timeshift = wtimeshift;
			if(wseparator != null){
				separator = new String(wseparator);
			}
			else{
				separator = new String(",");
			}
			header = wheader;
			time = wtime;
			millseccomma = wmillseccomma;
			showresult = wshowresult;
			Cp = cp;
		}
		/**
		 * 
		 */
		public void run(){
			fileReadingProgressDialog.reset();
			if(mode==0) {
				retcounter = doExportCSV( st, en, separator, file, hs, header, time, millseccomma, timeshift, showresult, fileReadingProgressDialog,decimation,decimationmode,Cp);
			}
			else{
				retcounter = doExportBin( st, en, file, hs, timeshift, showresult, fileReadingProgressDialog,decimation,decimationmode,littleendian);
			}
			fileReadingProgressDialog.setVisible(false);
			fileReadingProgressDialog.setTitle("Reading file...");

		}
	}
	// -------
	// -------------------------------------------------------------------------------------------

	// -------------------------------------------------------------------------------------------
	// -------
	/**
	 * win形式でのエクスポート
	 * @param st
	 * 開始時刻
	 * @param en
	 * 終了時刻
	 * @param file
	 * 出力ファイル
	 * @param hs
	 * ｃｈのリスト
	 * @param timeshift
	 * シフトする時間 
	 * @return
	 */
	private int doExportWINA0(wintime st,wintime en,File file,ArrayList<Integer> hs,double timeshift,ProgressDialog dlg,int chid_shift){
		
		int retcounter = 0;
		boolean error = false;
		
	// --------------------------------
		// ファイルをオープン
		FileOutputStream fw=null;
		try{
			fw = new FileOutputStream(file);
			/////////////////////////////////
			wintime tm = new wintime(st);// 1秒前のデータ
			tm.dec_sec();
			wintime tm2 = new wintime(st);//　これが該当秒のデータ 
			int latesttime = en.getValueS();
			// time shiftを１秒と指定すると、出力されるデータの時刻は元のデータの軸を1秒進めた時刻となる。
			// 秒と秒以下に分ける
			int deltasec = (int)(timeshift);
			double deltasub  = (timeshift-(double)deltasec); 
			// 1u秒以内はゼロとみなす
			if(deltasub<= 1e-6 && deltasub>=-1e-6) deltasub=0.0;
			int sigsub = 0;
			if(deltasub > 0.0){
				sigsub =1;
			}
			else if(deltasub < 0.0){
				sigsub=-1;
				deltasec--;
				latesttime++;
				deltasub = deltasub+1;
			}

			wintime tmsft = new wintime(st);
			tmsft.inc_sec(deltasec); // 表示用のあたらしい時刻（秒部分）
	//		wintime tmsftorg = new wintime(tmsft);
			if(dlg!=null){
				dlg.setText1(file.getAbsolutePath());
				dlg.getProgressBar1().setMinimum(tm2.getValueS());
				dlg.getProgressBar1().setMaximum(latesttime);
				dlg.getProgressBar2().setMinimum(0);
			}
			
		
			wintime startTime=null;
			wintime startTimeSft=null;
			wintime endTime=new wintime(tm2);
			wintime endTimeSft=new wintime(tmsft);
			boolean dataEmpty=true;
			
			int sampletime=0;
			int seccount=0;
			int cursec = 0;
			int writeCounter=0;
			ArrayList<int[]> hdata = new ArrayList<int[]>();
			ArrayList<int[]> hdata2 = new ArrayList<int[]>();
			// 時間順にループ
			boolean firstsec=true;
			while((cursec = tm2.getValueS()) <= latesttime){
				if(dlg!=null){
					if(dlg.isCanceled()){
						error = true;
						break;
					}
					dlg.setText2(tm2.fmt()+" : "+retcounter + "byte");
					dlg.getProgressBar1().setValue(cursec);
				}
				memoryManagement(100);
				
				// 一秒分を単位にループ
				hdata.clear();
				hdata2.clear();
				sampletime = getExportDataArrayList(hs,tm,tm2,hdata,hdata2);
				if(sampletime<0){
					error = true;
					break;
				}
				else if(sampletime!=0){
					dataEmpty=false;
					if(startTime==null){
						startTime = new wintime(tm2);
					}
					if(startTimeSft==null){
						startTimeSft = new wintime(tmsft);
					}
					endTime=new wintime(tm2);
					endTimeSft=new wintime(tmsft);
					double ticktime = 1/(double)sampletime;
				    // 読み飛ばすサンプル数
					int skipsample = (int)((deltasub / ticktime) + 0.5);
					if(dlg!=null) dlg.getProgressBar2().setMaximum(sampletime);
					
					int blocksize=4+6;// block size=4,sec header=6
					ArrayList<byte []> dlist = new ArrayList<byte []>();
					///Iterator<int []> datite = hdata.iterator(); // WINエクスポートで先頭の１秒分がエクスポートされない問題
					Iterator<int []> datite = hdata2.iterator(); // WINエクスポートで先頭の１秒分がエクスポートされない問題
					int chcount=0;
					while(datite.hasNext()){
						int [] data = datite.next();
						int [] data2 =hdata2.get(chcount);
						int[] odata = new int[sampletime];
						//
						//if(data==null || data2==null){
						//	continue;	
						//}
						boolean firstch=firstsec;
						for(int i=0;i<sampletime;i++){
							if((i)<skipsample){
								if(data==null) 	odata[i] = 0;
								else{
									//if(sigsub<0.0) 	odata[i] = data[i+(sampletime - skipsample)];
									//else 			odata[i] = data[i+(skipsample)];
									if(firstsec && firstch && sigsub != 0.0) odata[i] = 0;
									else odata[i] = data[i+(sampletime - skipsample)];
								}
							}
							else{
								firstch=false;
								if(data2==null) odata[i]=0;
								else 			odata[i] = data2[i-(skipsample)];						
							}
							retcounter++;
							//fw.write(mes+"\n");
							//System.out.println(mes);
							if(dlg!=null) dlg.getProgressBar2().setValue(i);
						}
						int chid = hs.get(chcount) + chid_shift;
						byte[] enc = wintools.makeChEncodeData(odata, chid);
						blocksize+=enc.length;
						dlist.add(enc);
						chcount++;
					}
					if(blocksize>=14){
						byte[] fdat=wintools.getBlockSize(blocksize);
						fw.write(fdat);
						fdat = wintools.makeSecHeader(tmsft);
						fw.write(fdat);
						Iterator<byte[]> ite=dlist.iterator();
						while(ite.hasNext()){
							fdat = ite.next();
							fw.write(fdat);
						}
						writeCounter++;
					}
					seccount++;
				}
				firstsec=false;
				tm.inc_sec();
				tm2.inc_sec();
				tmsft.inc_sec();
				
				if(writeCounter % 600 == 0){
					fw.flush();
					memoryManagement(retcounter/1000);
				}
				
			}
			fw.close();
			
			if(dlg!=null){
				if(!error){
					String msg = "Export WIN A0 ";
					if(dataEmpty){
						msg += "\n no data";
					}
					else{
						msg = String.format("success.\nFile=%s\n %,d sec\n %,d channel\nShift   = %,g sec\n", file.getAbsolutePath(),seccount,hs.size(),timeshift);
						//	msg += "Original data = " + st.fmt()+ " - "+en.fmt()+"\n";
						//	msg += "Shifted data  = " +tmsftorg.fmt() + " - " + tmsft.fmt();
						msg += "Original data = " + startTime.fmt()    + " - " + endTime.fmt()+"\n";
						msg += "Shifted data  = " + startTimeSft.fmt() + " - " + endTimeSft.fmt();
					}
					JOptionPane.showMessageDialog(null,msg);
				}
				else{
					String msg = String.format("Error : Export WIN A0" );
					JOptionPane.showMessageDialog(null,msg);
				}
			}
			
		} catch (IOException e){
			e.printStackTrace();
			error = true;
			retcounter=-1;
		}
		return retcounter;	
		// ret = retcounter;
	 }
	public int  exportWINA0(wintime st,wintime en,File file,ArrayList<Integer> hs,double timeshift){
		exportWINA0Thead thread = new exportWINA0Thead(st,en,file,hs,timeshift);
		
		if(DataSetter!=null){
			Point pt = DataSetter.getLocationOnScreen();
			pt.x  += 100;
			pt.y += 100;
			fileReadingProgressDialog.setLocation(pt);
		}
		new Thread(thread).start();
		fileReadingProgressDialog.setTitle("Writing WIN A0...");
		fileReadingProgressDialog.setVisible(true);
		return 0;
	}
	/**
	 * WINA0をエクスポートするスレッド派生クラス
	 * @author ikeda
	 *
	 */
	class exportWINA0Thead extends Thread{
		//public TreeSet<String> treeset =null;
		private wintime st = null;
		private wintime en = null;
		private File file = null;
		private ArrayList<Integer> hs = null;
		private double timeshift = 0;
		
		exportWINA0Thead(wintime wst,wintime wen,File wfile,ArrayList<Integer> whs,double wtimeshift){
			st = new wintime(wst);
			en = new wintime(wen);
			String fn = wfile.getAbsolutePath();
			file = new File(fn);
			hs  = new ArrayList<Integer>();
			Iterator<Integer> ite = whs.iterator();
			while(ite.hasNext()){
				hs.add(ite.next());
			}
			timeshift = wtimeshift;
		}
		public void run(){
			fileReadingProgressDialog.reset();
			doExportWINA0( st, en, file,hs, timeshift,fileReadingProgressDialog,0);
			fileReadingProgressDialog.setVisible(false);
			fileReadingProgressDialog.setTitle("Reading file...");

		}
	}
	/*
	 * ImportDataからデータをセットする。
	 */
	public void setData(ImportData dt) {
		// TODO: ImportDataからデータをセットする処理をつくる
		
		
		
	}
	/**
	 * Property のロードセーブ
	 * @param save
	 */
	public void LoadSaveProperty(boolean save) {
		if(save){
			int max =OpenHistory.size();
			int i=0;
			if(max>winchkgProperty.RecentFilesMax){
				i = max-winchkgProperty.RecentFilesMax;
			}
			int c=0;
			for(;i<max;i++){
				TreeSet<String> ts = OpenHistory.get(i);
				Iterator<String> ite = ts.iterator();
				StringBuilder mes = new StringBuilder();
				//String mes = new String();
				
				//if(ts.size()<=60){
					while(ite.hasNext()){
						String s = ite.next();
						if(s.length()>0){
							//mes += s +"\t";
							mes.append(s+"\t");// +=  s +"\t"; // ファイルのリストをタブ区切りにしておく
						}
					}
					winchkgProperty.RecentFiles[c++] = new String(mes.toString());
					//winchkgProperty.RecentFiles[c++] = mes;
				//}
			}
		}
		else{
			for(int j=0;j<winchkgProperty.RecentFilesMax;j++){
				String mes = winchkgProperty.RecentFiles[j];
				String[] m = mes.split("\t");
				TreeSet<String> ts = new TreeSet<String>();
				for(int i=0;i<m.length;i++){
					if(m[i].length()>0){
						ts.add(m[i]);
					}
				}
				if(ts.size()>0){
					OpenHistory.add(ts);
				}
				
			}
		}
		
		
	}
	/**
	 * channel ID を変更する。
	 * 　winファイルを作ってそれを読み込む
	 * @param id1  元のＩＤ
	 * @param id2　変換先ＩＤ
	 */
	public void changeChannelID(int id1, int id2) {
		int idx1 = Channel.indexOf(id1);
		if(idx1<0) return;
		
		wintime st = new wintime(OldestTime);
		wintime en = new wintime(LatestTime);
		File file = new File(getWriteOutFolder()+"_" +  String.format("%04x",id1) +"_" + String.format("%04x",id2) +".win");
		ArrayList<Integer> hs = new ArrayList<Integer>();
		hs.add(id1);
		doExportWINA0(st,en,file,hs,0.0,null,(id2-id1)%0x10000);
		addChData(file.getPath());
		clearChannelData(id1);
		
		int idx2 = Channel.indexOf(id2);
		Channel.remove(idx2);
		Channel.add(idx1,id2);
		
	}
	/**
	 * channel ID をすべて変更する。
	 * 
	 * @param offset
	 */
	public void changeChannelID(int offset) {
		
		for(int idx = 0;idx < Channel.size();idx++){
			int id1 = Channel.get(idx);
			int id2 = id1+offset;
			changeChannelID(id1,id2);
		}
	}

}
//-------
// -------------------------------------------------------------------------------------------


